Skip to content

feat: API clients will now return Requestable type instead of Request making writing tests easy #1094

Merged
NandanPrabhu merged 5 commits intodevelop/v3.0from
feat/requestable_protocol
Mar 5, 2026
Merged

feat: API clients will now return Requestable type instead of Request making writing tests easy #1094
NandanPrabhu merged 5 commits intodevelop/v3.0from
feat/requestable_protocol

Conversation

@NandanPrabhu
Copy link
Copy Markdown
Contributor

@NandanPrabhu NandanPrabhu commented Feb 20, 2026

Rationale behind the feature

Currently, the methods of the Authentication API client return a Request value. Since it’s a struct, it’s not really mockable. This makes it harder to test the (native) bridge code of those SDKs that wrap around Auth0.swift, like auth0-flutter –including any custom wrappers our customers write.

Till now to mock the Auth0 SDK layer, developers had to use URLProtocol to mock URLSession for writing unit tests on the client app or wrapper SDK. With the Requestable protocol, developers can now do not have to do the heavy lifting of mocking URLSession for writing unit tests. It makes it super easy.

This PR promotes Requestable to the primary request interface across the SDK, so API clients return any Requestable<T, E> instead of Request<T, E>, improving ergonomics around mocking/testing and enabling existential-friendly chaining plus async/await and Combine support.

Changes:

  1. Update public client protocols + concrete implementations to return any Requestable<..., ...> instead of Request<..., ...>.
  2. Expand Requestable to include builder methods plus async/await and Combine start() overloads; adjust Request builder return types accordingly.
  3. Update internal JWKS request storage and adjust tests to work with protocol existentials.

Tests Updated

  • Auth0Tests/MFA/Auth0MFAClientTests.swift — 13 local variable type annotations changed from let request: Request<T, E> to let request: any Requestable<T, E>.
  • Auth0Tests/AuthenticationSpec.swift — Added a private extension Requestable that exposes dpop: DPoP? via a conditional downcast to Request<T, E>, allowing DPoP-related test assertions to continue working on any Requestable<T, E> values without polluting the public API.
  • Auth0Tests/RequestSpec.swift — Tests that verify the stored parameters, headers, and dpop properties after calling a builder method now cast the builder result back to Request<T, E> (e.g. as! Request<[String: Any], AuthenticationError>) before inspecting internal state. These tests are specifically exercising Request<T, E> internals, so the explicit downcast makes the intent clear.
  • Tests added for sample app ContentViewModel demonstrating usage of Requestable protocol for mocking

Migration Impact

Source-breaking for callers who store the return value with an explicit Request<T, E> type annotation. All other usage patterns (chaining, start {}, try await .start(), Combine .start()) remain unaffected.

// Before — explicit type annotation:
let request: Request<Credentials, AuthenticationError> = auth.login(...)

// After — use the protocol type or rely on type inference:
let request: any Requestable<Credentials, AuthenticationError> = auth.login(...)
// or simply:
let request = auth.login(...)

@NandanPrabhu NandanPrabhu marked this pull request as ready for review February 20, 2026 04:55
@NandanPrabhu NandanPrabhu requested a review from a team as a code owner February 20, 2026 04:55
@sanchitmehtagit sanchitmehtagit added Swift v3.0 This label depicts this feature is part of Swift 3.0 review:medium Medium review labels Feb 20, 2026
@NandanPrabhu NandanPrabhu force-pushed the feat/requestable_protocol branch from 10b820c to 22af999 Compare February 23, 2026 04:00
@NandanPrabhu NandanPrabhu marked this pull request as draft February 23, 2026 04:38
@NandanPrabhu NandanPrabhu force-pushed the feat/requestable_protocol branch from 9d3cbfc to 562b50e Compare February 25, 2026 15:22
@NandanPrabhu NandanPrabhu force-pushed the feat/requestable_protocol branch from e504eeb to 367ef1b Compare February 25, 2026 15:42
@NandanPrabhu NandanPrabhu marked this pull request as ready for review February 25, 2026 15:42
@NandanPrabhu NandanPrabhu force-pushed the feat/requestable_protocol branch 2 times, most recently from 2c8db8b to 8718d8b Compare February 25, 2026 17:03
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR promotes Requestable to the primary request interface across the SDK, so API clients return any Requestable<T, E> instead of Request<T, E>, improving ergonomics around mocking/testing and enabling existential-friendly chaining plus async/await and Combine support.

Changes:

  • Update public client protocols + concrete implementations to return any Requestable<..., ...> instead of Request<..., ...>.
  • Expand Requestable to include builder methods plus async/await and Combine start() overloads; adjust Request builder return types accordingly.
  • Update internal JWKS request storage and adjust tests to work with protocol existentials.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
Auth0/Requestable.swift Makes Requestable generic w/ primary associated types; adds builder + Combine/async requirements + defaults.
Auth0/Request.swift Adjusts builder method return types to any Requestable<T, E> to satisfy updated protocol.
Auth0/Authentication.swift Updates Authentication protocol + convenience overloads to return any Requestable.
Auth0/Auth0Authentication.swift Updates concrete Authentication client return types to any Requestable.
Auth0/Users.swift Updates Users API protocol, default-arg extension, and Management implementation return types.
Auth0/MFA/MFAClient.swift Updates MFAClient protocol return types to any Requestable.
Auth0/MFA/Auth0MFAClient.swift Updates concrete MFA client (and helper token()) return types to any Requestable.
Auth0/MyAccount/AuthenticationMethods/MyAccountAuthenticationMethods.swift Updates MyAccount auth-methods protocol + default-arg extension return types.
Auth0/MyAccount/AuthenticationMethods/Auth0MyAccountAuthenticationMethods.swift Updates concrete MyAccount auth-methods implementation return types.
Auth0/IDTokenSignatureValidator.swift Changes jwksRequest to any Requestable in validator context protocol.
Auth0/IDTokenValidatorContext.swift Changes stored jwksRequest type to any Requestable.
Auth0Tests/MFA/Auth0MFAClientTests.swift Updates request variable annotations to any Requestable to match new API.
Auth0Tests/RequestSpec.swift Updates tests to downcast builder results back to Request to assert internals.
Auth0.xcodeproj/project.pbxproj Tweaks SWIFT_ACTIVE_COMPILATION_CONDITIONS (notably WEB_AUTH_PLATFORM) across some configs.
App/ContentViewModel.swift Refactors #if WEB_AUTH_PLATFORM blocks; changes web login/logout implementation.
App/ContentView.swift Minor conditional compilation / formatting adjustments in the sample app UI.
Comments suppressed due to low confidence (1)

App/ContentViewModel.swift:23

  • webLogin(presentationWindow:) still accepts a presentationWindow but no longer applies it to Auth0.webAuth(). On macOS this can prevent ASWebAuthenticationSession from presenting correctly (and it also leaves an unused parameter). Please either pass the window through via .presentationWindow(window) when non-nil or remove the parameter/call sites if it’s no longer needed.
    func webLogin(presentationWindow window: Auth0WindowRepresentable? = nil) async {
        isLoading = true
        errorMessage = nil

        do {

            let credentials = try await Auth0
                .webAuth()
                .scope("openid profile email offline_access")
                .start()


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 55 to 56
let request = Request().parameters(["foo": "bar"]) as! Request<[String: Any], AuthenticationError>
expect(request.parameters["foo"] as? String) == "bar"
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests use as! Request<...> to downcast from the builder result. If the cast ever fails, it will crash the entire test run rather than producing a clean assertion failure. Consider using a conditional cast (as?) and asserting it’s non-nil before inspecting internals, so failures are reported as test failures with clearer diagnostics.

Copilot uses AI. Check for mistakes.
scope: String,
organization: String?,
parameters: [String: Any]) -> Request<Credentials, AuthenticationError>
parameters: [String: Any])-> any Requestable<Credentials, AuthenticationError>
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s a formatting issue in the function signature: parameters: [String: Any])-> is missing whitespace before ->. This is likely to trip SwiftLint’s operator whitespace rule for files under Auth0/ and makes the signature harder to read. Please add a space so it matches the surrounding style (parameters: [String: Any]) -> ...).

Suggested change
parameters: [String: Any])-> any Requestable<Credentials, AuthenticationError>
parameters: [String: Any]) -> any Requestable<Credentials, AuthenticationError>

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +17

extension Requestable {
var dpop: DPoP? { (self as? Request<ResultType, ErrorType>)?.dpop }
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requestable is being extended with an internal dpop computed property that downcasts to Request. This couples the protocol to a concrete type and (based on current usages) appears to exist only to support test assertions. Consider moving this helper into the test target (e.g., a @testable-only extension) or gating it behind #if DEBUG to avoid shipping unused/internal-only API in the library module.

Suggested change
extension Requestable {
var dpop: DPoP? { (self as? Request<ResultType, ErrorType>)?.dpop }
}
#if DEBUG
extension Requestable {
var dpop: DPoP? { (self as? Request<ResultType, ErrorType>)?.dpop }
}
#endif

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is removed

PRODUCT_BUNDLE_IDENTIFIER = com.auth0.OAuth2;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The iOS app target’s Release build configuration sets SWIFT_ACTIVE_COMPILATION_CONDITIONS = "", while Debug defines WEB_AUTH_PLATFORM. Since ContentView/ContentViewModel wrap login/logout code in #if WEB_AUTH_PLATFORM, this will change app behavior between Debug and Release (web auth UI disappears in Release). If that’s not intentional, include WEB_AUTH_PLATFORM in Release as well (and likewise for other app targets/configs where you expect web auth).

Suggested change
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = WEB_AUTH_PLATFORM;

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

@NandanPrabhu NandanPrabhu Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sample app will never be released. since we have one unified sample app for all the platforms, compiler flags are required as tvos and watchos doesn't support web authn but only for debug mode

associatedtype ResultType
associatedtype ErrorType: Auth0APIError
func start(_ callback: @escaping (Result<ResultType, ErrorType>) -> Void)
func parameters(_ extraParameters: [String: Any]) -> any Requestable<ResultType, ErrorType>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please validate mockability by test suite

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes validated it in sample app

PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = WEB_AUTH_PLATFORM;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes @sanchitmehtagit since we have one unified sample app for all the platforms we need this compiler flags for enabling web auth

var issuer: String { get }
var audience: String { get }
var jwksRequest: Request<JWKS, AuthenticationError> { get }
var jwksRequest: any Requestable<JWKS, AuthenticationError> { get }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe This is an internal protocol we don't need a change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IdtokenValidatorContext is internal. Using any Requestable will not cause any impact on the visibility.

sanchitmehtagit
sanchitmehtagit previously approved these changes Mar 3, 2026
@NandanPrabhu NandanPrabhu force-pushed the feat/requestable_protocol branch 2 times, most recently from a30622b to 46404b0 Compare March 3, 2026 11:17
sanchitmehtagit
sanchitmehtagit previously approved these changes Mar 3, 2026
@sanchitmehtagit sanchitmehtagit changed the title All the API client will now return Requestable instead of Request making writing tests easy feat: All the API client will now return Requestable instead of Request making writing tests easy Mar 3, 2026
@NandanPrabhu NandanPrabhu force-pushed the feat/requestable_protocol branch from 46404b0 to f2fc11f Compare March 3, 2026 16:14
@NandanPrabhu NandanPrabhu changed the title feat: All the API client will now return Requestable instead of Request making writing tests easy feat: API clients will now return Requestable type instead of Request making writing tests easy Mar 3, 2026
@NandanPrabhu NandanPrabhu force-pushed the feat/requestable_protocol branch from 96ab7d2 to 04015c9 Compare March 4, 2026 15:38
@NandanPrabhu NandanPrabhu merged commit 6f54061 into develop/v3.0 Mar 5, 2026
17 of 18 checks passed
@NandanPrabhu NandanPrabhu deleted the feat/requestable_protocol branch March 5, 2026 04:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review:medium Medium review Swift v3.0 This label depicts this feature is part of Swift 3.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants